
using Nop.Core;
using Nop.Core.Caching;
using Nop.Core.Data;
using Nop.Core.Domain.Directory;
using Nop.Services.Catalog;
using Nop.Services.Events;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Nop.Services.Directory
{
	/// <summary>
	/// Department service
	/// </summary>
	public partial class DepartmentService : IDepartmentService
	{
		#region Constants

		/// <summary>
		/// Key for caching
		/// </summary>
		private const string DEPARTMENTS_ALL_KEY = "Nop.department.all";
		/// <summary>
		/// Key for caching
		/// </summary>
		/// <remarks>
		/// {0} : department ID
		/// </remarks>
		private const string DEPARTMENT_BY_ID_KEY = "Nop.department.id-{0}";
		/// <summary>
		/// Key pattern to clear cache
		/// </summary>
		private const string DEPARTMENTS_PATTERN_KEY = "Nop.department.";
		#endregion

		#region Fields
		private readonly IRepository<Department> _departmentRepository;
		private readonly IEventPublisher _eventPublisher;
		private readonly ICacheManager _cacheManager;
		#endregion

		#region Ctor

		/// <summary>
		/// Ctor
		/// </summary>
		/// <param name="cacheManager">Cache manager</param>
		/// <param name="departmentRepository">Department repository</param>
		/// <param name="eventPublisher">Event published</param>
		public DepartmentService(ICacheManager cacheManager,
			IRepository<Department> departmentRepository,
			IEventPublisher eventPublisher)
		{
			this._cacheManager = cacheManager;
			this._departmentRepository = departmentRepository;
			this._eventPublisher = eventPublisher;
		}

		#endregion

		#region Methods

		/// <summary>
		/// Gets all Departments
		/// </summary>
		/// <returns>Departments</returns>
		public virtual IList<Department> GetAllDepartments()
		{
			string key = DEPARTMENTS_ALL_KEY;
			return _cacheManager.Get(key, () =>
			{
				var query = from a in _departmentRepository.Table
							where !a.Deleted
							&& a.Active
							orderby a.Name
							select a;
				return query.ToList();
			});
		}

		public virtual IPagedList<Department> GetAllDepartments(int pid = -1, string name = "", bool showHidden = true, bool? isMaintenance = null, int pageIndex = 0, int pageSize = int.MaxValue)
		{
			var query = _departmentRepository.Table;
			if (pid > -1)
				query = query.Where(m => m.PId == pid);
			if (!string.IsNullOrEmpty(name))
				query = query.Where(m => m.Name.Contains(name));
			if (!showHidden)
				query = query.Where(c => c.Active);
			if (isMaintenance.HasValue)
				query = query.Where(c => c.IsMaintenance == isMaintenance.Value);
			query = query.Where(c => !c.Deleted);
			query = query.OrderByDescending(c => c.Id);

			var unsortedEquipments = query.ToList();

			//sort
			var sortedEquipments = unsortedEquipments.SortDepartmentsForTree();

			//paging
			return new PagedList<Department>(sortedEquipments, pageIndex, pageSize);
		}

		public virtual IList<Department> GetAllDepartments(int departmentId)
		{
			var c = GetDepartmentById(departmentId);
			if (null != c && !c.Deleted)
				return GetAllDepartments(c);
			return new List<Department>();
		}

		public virtual IList<Department> GetAllDepartments(Department department)
		{
			var departments = new List<Department> { department };
			var childrens = _departmentRepository.Table.Where(m => !m.Deleted && m.Active && m.PId == department.Id).ToList();
			foreach (var child in childrens)
			{
				departments.AddRange(GetAllDepartments(child));
			}
			return departments;
		}

		/// <summary>
		/// Gets a Department 
		/// </summary>
		/// <param name="departmentId">Department identifier</param>
		/// <returns>Department</returns>
		public virtual Department GetDepartmentById(int departmentId)
		{
			if (departmentId == 0)
				return null;

			string key = string.Format(DEPARTMENT_BY_ID_KEY, departmentId);
			return _cacheManager.Get(key, () => _departmentRepository.GetById(departmentId));
		}

		public virtual bool EqualAbbr(int id, string abbr)
		{
			var query = _departmentRepository.Table.FirstOrDefault(m => m.Id == id && m.Abbr == abbr);
			return query != null;
		}

		public virtual int GetIdByAbbr(string abbr)
		{
			var query = _departmentRepository.Table.FirstOrDefault(m => m.Abbr == abbr);
			return query == null ? 0 : query.Id;
		}

		public virtual Department GetDepartmentByAbbr(string abbr)
		{
			if (string.IsNullOrEmpty(abbr))
				return null;
			return _departmentRepository.Table.FirstOrDefault(m => !m.Deleted && m.Active && m.Abbr == abbr);
		}

		/// <summary>
		/// Inserts a Department
		/// </summary>
		/// <param name="department">Department</param>
		public virtual void InsertDepartment(Department department)
		{
			if (department == null)
				throw new ArgumentNullException("department");

			_departmentRepository.Insert(department);

			_cacheManager.RemoveByPattern(DEPARTMENTS_PATTERN_KEY);

			//event notification
			_eventPublisher.EntityInserted(department);
		}

		/// <summary>
		/// Updates the Department
		/// </summary>
		/// <param name="department">Department</param>
		public virtual void UpdateDepartment(Department department)
		{
			if (department == null)
				throw new ArgumentNullException("department");

			_departmentRepository.Update(department);

			_cacheManager.RemoveByPattern(DEPARTMENTS_PATTERN_KEY);

			//event notification
			_eventPublisher.EntityUpdated(department);
		}

		/// <summary>
		/// Deletes a Department
		/// </summary>
		/// <param name="department">Department</param>
		public virtual void DeleteDepartment(Department department)
		{
			if (department == null)
				throw new ArgumentNullException("department");

			//_departmentRepository.Delete(department);

			//_cacheManager.RemoveByPattern(DEPARTMENTS_PATTERN_KEY);

			department.Deleted = true;
			UpdateDepartment(department);

			//event notification
			_eventPublisher.EntityDeleted(department);

			var subcategories = GetAllDepartments(pid: department.Id);
			foreach (var subcategory in subcategories)
			{
				DeleteDepartment(subcategory);
			}
		}

		public virtual string GetNameById(int id)
		{
			var query = GetDepartmentById(id);
			return query == null ? "" : query.Name;
		}

		public virtual bool IsSame(int depId, string abbr)
		{
			return _departmentRepository.Table.Any(d => d.Id == depId && d.Abbr == abbr);
		}

		#endregion
	}
}
